
#ifndef STOPPINGCRITERION_H

#include <gsl_ext_cpp/GSLFunctionWrapper.h>
#include <gsl_ext_cpp/GSLMinimizerWrapper.h>

#include <gsl/gsl_multimin.h>

/// Defines the interface for different stopping criteria.
class StoppingCriterion
{
public:
  /// Constructs a new stopping criterion with threshold value eps.
  StoppingCriterion(double eps);
  
  virtual ~StoppingCriterion() { }
    
  /// Given a minimizer object, computes the value that is tested against the threshold.
  virtual double getThresholdValue(const GSLMinimizerWrapper &s) const = 0;
  
  /// Returns true, if this stopping criterion can be used with the given function, false if not.
  virtual bool isCompatibleWith(const GSLFunctionWrapper &f) const = 0;
  
  /// Returns true, if this stopping criterion can be used with the given minimizer, false if not.
  virtual bool isCompatibleWith(const GSLMinimizerWrapper &s) const = 0;
  
  /// Tests the given minimizer against this stopping criterion. 
  /**
   * This method tests the state of the given minimizer against this 
   * stopping criterion. Returns GSL_SUCCESS, if the criterion is 
   * satisfied, GSL_CONTINUE otherwise.
   */
  virtual int test(const GSLMinimizerWrapper &s) const = 0;
protected:
  /// Threshold value.
  const double EPS_;
  
  /// Check that the given minimizer is compatible. If not, throw an exception.
  void checkCompatibility(const GSLMinimizerWrapper &s) const;
private:
  // These are not implemented, so using them is disallowed.
  StoppingCriterion(const StoppingCriterion &sc);
  StoppingCriterion &operator=(const StoppingCriterion &sc);
};

/// Tests the difference of the current function value and the minimum value.
/**
 * This class is for testing the difference of the current and minimum function 
 * values, i.e. \f$\|f(\mathbf{x}_{k})-f(\mathbf{x}^{*})\|\f$.
 */
class FDistToMinTest:public StoppingCriterion
{
public:
  FDistToMinTest(double fMin, double eps, bool relative);
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
private:
  const double F_MIN_;
  const bool RELATIVE_;
};

/// Tests the gradien norm.
/**
 * This class is for testing the gradient norm 
 * \f$\|\nabla f(\mathbf{x}_{k})\|\f$.
 */
class GradientNormTest:public StoppingCriterion
{
public:
  GradientNormTest(double eps);
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
};

/// Tests if the maximum number of iterations has been reached.
class MaxNumIterTest:public StoppingCriterion
{
public:
  MaxNumIterTest(int n);
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
private:
  const int MAX_NUM_ITER_;
};

/// Tests the simplex size.
/**
 * For the gsl_nmsimplex algorithm, this criterion tests the 
 * average of simplex side lengths. For the gslpp_lrwwsimplex 
 * algorithm, this criterion tests the simplex volume defined 
 * as the determinant of the edge matrix.
 * @see gsl_nmsimplex.c and gslpp_lrwwsimplex.c
 */
class SimplexSizeTest:public StoppingCriterion
{
public:
  SimplexSizeTest(double eps);
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
};

/// Tests the difference between two successive iterates.
/**
 * This class is for testing the difference between two successive 
 * iterates, i.e. \f$\|\mathbf{x}_{k+1}-\mathbf{x}_{k}\|\f$.
 */
class XDiffTest:public StoppingCriterion
{
public:
  XDiffTest(double eps);
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
};

/// Tests the distance to minimizer.
/**
 * This class is for testing the distance of the current 
 * iterate to the user-supplied minimizer, i.e. 
 * \f$\|\mathbf{x}_{k}-\mathbf{x}^{*}\|\f$.
 */
class XDistToMinTest:public StoppingCriterion
{
public:
  XDistToMinTest(gsl_vector *xMin, double eps, bool relative);
  ~XDistToMinTest();
  
  double getThresholdValue(const GSLMinimizerWrapper &s) const;
  bool isCompatibleWith(const GSLFunctionWrapper &f) const;
  bool isCompatibleWith(const GSLMinimizerWrapper &s) const;
  int test(const GSLMinimizerWrapper &s) const;
private:
  XDistToMinTest(const XDistToMinTest &t);
  XDistToMinTest &operator=(const XDistToMinTest &t);
  
  const bool RELATIVE_;
  gsl_vector *X_MIN_;
  gsl_vector *tmpv_;
};

#define STOPPINGCRITERION_H

#endif
